'use strict';

// options param: {username, chaincodeId, fcn, args, channelId, org, host,orderHost}
module.exports = async function (options) {
    const Fabric_Client = require('fabric-client');
    const path = require('path');
    const util = require('util');
    try {
        const fabric_client = new Fabric_Client();
        const channel = fabric_client.newChannel(options.channelId);
        const peer = fabric_client.newPeer('grpc://' + options.host + ':7051');
        channel.addPeer(peer);
        const order = fabric_client.newOrderer('grpc://' + options.orderHost + ':7050')
        channel.addOrderer(order);

        let member_user = null;
        const store_path = path.join(__dirname, 'hfc-key-store' + options.org);
        console.log('Store path:' + store_path);
        let tx_id = null;
        const state_store = await Fabric_Client.newDefaultKeyValueStore({path: store_path});
        fabric_client.setStateStore(state_store);
        const crypto_suite = Fabric_Client.newCryptoSuite();
        const crypto_store = Fabric_Client.newCryptoKeyStore({
            path: store_path
        });
        crypto_suite.setCryptoKeyStore(crypto_store);
        fabric_client.setCryptoSuite(crypto_suite);
        const user_from_store = await fabric_client.getUserContext(options.username, true);
        if (user_from_store && user_from_store.isEnrolled()) {
            console.log('Successfully loaded' + options.username + ' from persistence');
            member_user = user_from_store;
        } else {
            console.log('Failed to get ' + options.username + '.... run invoke.js');
            return;
        }
        tx_id = fabric_client.newTransactionID();
        console.log("Assigning transaction_id: ", tx_id._transaction_id);
        const request = {
            chaincodeId: options.chaincodeId,
            fcn: options.fcn,
            args: options.args,
            chainId: options.channelId,
            txId: tx_id
        };

        const results = await channel.sendTransactionProposal(request);
        const proposalResponses = results[0];
        const proposal = results[1];
        let isProposalGood = false;
        if (proposalResponses && proposalResponses[0].response &&
            proposalResponses[0].response.status === 200) {
            isProposalGood = true;
            console.log('Transaction proposal was good');
        } else {
            console.error('Transaction proposal was bad');
        }
        if (isProposalGood) {
            console.log(util.format(
                'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s"',
                proposalResponses[0].response.status, proposalResponses[0].response.message));

            const request = {
                proposalResponses: proposalResponses,
                proposal: proposal
            };

            const transaction_id_string = tx_id.getTransactionID();
            let promises = [];

            const sendPromise = channel.sendTransaction(request);
            promises.push(sendPromise);

            let event_hub = channel.newChannelEventHub(options.host + ':7051');
            let txPromise = new Promise((resolve, reject) => {
                let handle = setTimeout(() => {
                    event_hub.disconnect();
                    resolve({
                        event_status: 'TIMEOUT'
                    });
                }, 3000);
                event_hub.connect();
                event_hub.registerTxEvent(transaction_id_string, (tx, code) => {
                    clearTimeout(handle);
                    event_hub.unregisterTxEvent(transaction_id_string);
                    event_hub.disconnect();
                    const return_status = {
                        event_status: code,
                        tx_id: transaction_id_string
                    };
                    if (code !== 'VALID') {
                        console.error('The transaction was invalid, code = ' + code);
                        resolve(return_status);
                    } else {
                        // console.log('The transaction has been committed on peer ' + event_hub._ep._endpoint.addr);
                        console.log('The transaction has been committed on peer ' + event_hub.getPeerAddr());
                        resolve(return_status);
                    }
                }, (err) => {
                    reject(new Error('There was a problem with the eventhub ::' + err));
                });
            });
            promises.push(txPromise);
            const results = await Promise.all(promises);
            if (results && results[0] && results[0].status === 'SUCCESS') {
                console.log('Successfully sent transaction to the orderer.');
                return results;
            } else {
                console.error('Failed to order the transaction. Error code: ' + response.status);
            }

            if (results && results[1] && results[1].event_status === 'VALID') {
                console.log('Successfully committed the change to the ledger by the peer');
            } else {
                console.log('Transaction failed to be committed to the ledger due to ::' + results[1].event_status);
            }
        }
    }catch (e) {
        console.log(e)
    }
};
